package net.quanter.shield.common.dto.result;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;

import static net.quanter.shield.common.enums.errors.ResultError.OBJECT_NOT_SUPPORT_CLONE;

/**
 * shield-common
 * Controller，Service，RPC等方法的统一返回对象包装类
 * create: 2019-06-14 09:33
 *
 * @author 王老实
 * @since 1.3.12.RELEASE
 */
@Schema(description = "接口统一返回数据封装")
@ToString(exclude = "x")
@EqualsAndHashCode(of = {"success", "message", "messageParams", "code", "data"})
public class ResultDTO<T> implements Serializable, Cloneable {


    private static final long serialVersionUID = 1L;
    private static final ResultDTO CLONE_FAIL = ResultDTO.failure(OBJECT_NOT_SUPPORT_CLONE.name(), OBJECT_NOT_SUPPORT_CLONE.code);
    public static final ResultDTO<Void> SUCCESS = ResultDTO.success(null);
    public static final ResultDTO<Void> FAILURE = ResultDTO.failureVoid();
    public static final ResultDTO<Boolean> TRUE = ResultDTO.success(Boolean.TRUE);
    public static final ResultDTO<Boolean> FALSE = ResultDTO.success(Boolean.FALSE);

    @Schema(description = "成功标识")
    @Setter
    private Boolean success;

    @Schema(description = "错误信息")
    @Setter
    @Getter
    private String message;

    @Schema(description = "错误信息替换变量")
    @Setter
    @Getter
    private Map<String, String> messageParams;

    @Schema(description = "状态码")
    @Setter
    @Getter
    private Integer code;

    @Schema(description = "数据")
    @Getter
    private T data;


    @Schema(description = "扩展信息(x代表未知-by 乘风破浪的姐姐1)")
    @Getter
    protected JSONObject x;

    public ResultDTO() {
    }

    public ResultDTO(boolean success) {
        this.success = success;
    }

    public ResultDTO(T data) {
        this.success = Boolean.TRUE;
        this.data = data;
    }

    public ResultDTO(boolean success, String message, Integer code, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
        this.code = code;
    }

    @SneakyThrows
    @Override
    public ResultDTO<T> clone() {
        try {
            return (ResultDTO<T>) super.clone();
        } catch (CloneNotSupportedException e) {
            return ResultDTO.CLONE_FAIL;
        }
    }

    public ResultDTO<T> deepClone(TypeReference<ResultDTO<T>> typeReference) throws IOException, ClassNotFoundException {
        if (data != null && !(data instanceof Serializable)) {
            //如果data不为null且没有实现Serializable接口，则使用json进行克隆
            String json = this.toJsonString();
            return JSON.parseObject(json, typeReference);
        } else {
            //如果data为null，或者data实现了Serializable接口,则使用序列化方式进行克隆
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (ResultDTO<T>) ois.readObject();
        }
    }

    public ResultDTO<T> putMessageParam(String key, String value) {
        if (messageParams == null) {
            messageParams = new HashMap<>();
        }
        messageParams.put(key, value);
        return this;
    }

    public ResultDTO<T> data(T data) {
        this.data = data;
        return this;
    }

    public void setData(T data) {
        data(data);
    }

    public ResultDTO<T> message(String message) {
        this.message = message;
        return this;
    }

    public ResultDTO<T> code(Integer code) {
        this.code = code;
        return this;
    }

    public ResultDTO<T> x(JSONObject x) {
        this.x = x;
        return this;
    }

    public JSONObject putX(String key, Object value) {
        if (this.x == null) {
            this.x = new JSONObject();
        }
        x.put(key, value);
        return x;
    }

    public Optional<T> getX(String key, Class<T> tClass) {
        if (this.x == null || !this.x.containsKey(key)) {
            return Optional.empty();
        } else {
            return Optional.of(this.x.getObject(key, tClass));
        }
    }

    public Optional<T> get() {
        return Optional.ofNullable(data);
    }

    public boolean isSuccess() {
        return Boolean.TRUE.equals(success);
    }

    public static <T extends Serializable> ResultDTO<T> success() {
        return new ResultDTO<T>(Boolean.TRUE);
    }

    public static <T> ResultDTO<T> success(T data) {
        return new ResultDTO<>(data);
    }

    public static <T extends Serializable> ResultDTO<T> failure() {
        return new ResultDTO<>(Boolean.FALSE, null, null, null);
    }

    public static ResultDTO<Void> failureVoid() {
        return new ResultDTO<Void>(Boolean.FALSE);
    }

    public static <T extends Serializable> ResultDTO<T> failure(String message, Integer code) {
        return new ResultDTO<>(Boolean.FALSE, message, code, null);
    }

    public static <T extends Serializable> ResultDTO<T> parseJson(String json, Type type) {
        return JSON.parseObject(json, type);
    }

    public static <T extends Serializable> ResultDTO<T> parseJson(String json, TypeReference<ResultDTO<T>> typeReference) {
        return JSON.parseObject(json, typeReference);
    }

    public String toJsonString() {
        return JSON.toJSONString(this);
    }
}
